/* -*- c -*-
 * This is a software implementation of a KISS TNC using Arduino Sketch
 * This implementation is based on a work done by folkert@vanheusden.com
 * This program is modified by Josef Matondang and is available from
 * https://github.com/josefmtd/kiss-lora.
 * If you have any questions of implementation of the software, please feel
 * free to contact me on josstemat@gmail.com
 * (Note, what you see here has been hacked on by Chris Baird..)
 */

#include "kiss.h"
#include <ESP8266WiFi.h>
#include <SPI.h>
#include "RH_RF95.h"

// commands to the TNC...
// setpower
// reset
// heartbeat on/off (controllable LED blink)

// fallback AP
// RSSI report
// IP Gatewaying and routing[?!]

/* To communicate with the LoRa device, RadioHead library is used. RadioHead
 * is ideal for this implementation since it can access LoRa device's packet
 * mode, in particular the Variable Length Packet Mode. This allows possibility
 * for using RadioHead to construct packet from 4-255 bytes. Current RH_RF95
 * implementation uses 4 bytes for Addressing.
 *
 * For more informations regarding RadioHead library you can find them in
 * http://www.airspayce.com/mikem/arduino/RadioHead/
 * This implementation uses RadioHead-1.83 which can be accessed from:
 * http://www.airspayce.com/mikem/arduino/RadioHead/RadioHead-1.83.zip
 */

#define pinReset 16		/* /RST Atmega: 9, ESP: 16 */
static RH_RF95 rf95 (15,5);	/* CS,DIO0 Atmega: 10,2  ESP: 15,5 */

const char* MY_WIFI_SSID = "VK2CJB/P";
const char* MY_WIF_PASSWORD = "postieeasyrider";
IPAddress local_IP     (192,168,1,25);
IPAddress gateway      (192,168,1,254);
IPAddress subnet       (255,255,255,0);
IPAddress primaryDNS   (192,168,1,254);
IPAddress secondaryDNS (202,142,142,142);

/* cold reboot after this many seconds without any traffic */
#define WATCHDOG 600
unsigned long int lastactivity = 0;

#define MAX_SRV_CLIENTS 8
WiFiClient serverClients[MAX_SRV_CLIENTS];
WiFiServer server (11235); /* Flowers have a thing for Fibonacci :) */

// ----------------------------------------------------------------------

bool peekRadio()
{
  return rf95.available();
}


void getRadio (uint8_t *const recv_buffer,
	       uint16_t *const recv_buffer_size)
{
  lastactivity = millis();
  // xxxcjb recv_buffer needs to come in with the maximum packet size..
  uint8_t dummy = *recv_buffer_size;
  rf95.recv(recv_buffer, &dummy);
  *recv_buffer_size = dummy;
}


void putRadio (const uint8_t *const send_buffer,
	       const uint16_t send_buffer_size)
{
  rf95.send (send_buffer, send_buffer_size);
  rf95.waitPacketSent();
}


bool initRadio (void)
{
  if (rf95.init())
    {
      delay(100);
      return true;
    }
  return false;
}


bool resetRadio (void)
{
  digitalWrite (pinReset, LOW);
  delay (1);
  digitalWrite (pinReset, HIGH);
  delay (5+1);			// Must wait 5 ms before init

  return initRadio();
}

// ----------------------------------------------------------------------

bool initNet (void)
{
  uint8_t i = 20;

  // void ESP8266WiFiGenericClass::setOutputPower(float dBm) {   dbm < 83
  WiFi.setOutputPower (10);

  if (!WiFi.config (local_IP, gateway, subnet, primaryDNS, secondaryDNS))
    // todo: a blinking LED
    delay (500);

  WiFi.begin (MY_WIFI_SSID, MY_WIF_PASSWORD);

  while (WiFi.status() != WL_CONNECTED && i--)
    delay (500);

  if (i == 0)
    return false;

  server.begin();
  server.setNoDelay (true);
  return true;
}

void updateNet (void)
{
  // check for new or missing clients
  if (server.hasClient())
    {
      for (uint8_t i = 0; i < MAX_SRV_CLIENTS; i++)
        //find those free/disconnected spot
        if (!serverClients[i] || !serverClients[i].connected())
          {
            if (serverClients[i])
              serverClients[i].stop();
            serverClients[i] = server.available();
            continue;
          }
      //no free/disconnected spot so reject
      WiFiClient serverClient = server.available();
      serverClient.stop();
    }
}


bool peekNet()
{
  updateNet();

  for (uint8_t i = 0; i < MAX_SRV_CLIENTS; i++)
    if (serverClients[i] && serverClients[i].connected() && serverClients[i].available())
      return true;
  return false;
}


bool getNet (uint8_t *const recv_buffer,
	     const uint16_t recv_buffer_size,
	     const unsigned long int time_out)
{
  updateNet();

  // check all clients for .available data, but only retrieve the
  // first.. it'll eventually loop to all of them.. maybe

  while (millis() < time_out)
    {
      uint16_t cr = 0;
      for (uint8_t i = 0; i < MAX_SRV_CLIENTS; i++)
	if (serverClients[i] && serverClients[i].connected())
	  {
	    // needs to handle fragmented packets
	    while (serverClients[i].available() && (cr < recv_buffer_size))
	      recv_buffer[cr++] = serverClients[i].read();
	    // quit scanning if anything from this client was read
	    if (cr)
	      {
		lastactivity = millis();
		return true;
	      }
	  }
    }
  return false;
}


void putNet (const uint8_t *const send_buffer,
	     const uint16_t send_buffer_size)
{
  updateNet();

  for (uint8_t i = 0; i < MAX_SRV_CLIENTS; i++)
    if (serverClients[i] && serverClients[i].connected())
      serverClients[i].write (send_buffer, send_buffer_size);
}

// ----------------------------------------------------------------------

kiss k (251, peekRadio, getRadio, putRadio, peekNet, getNet, putNet, resetRadio);

int ledPin = 02;

void setup (void)
{
  delay (10);
  pinMode (ledPin, OUTPUT);
  pinMode (pinReset, OUTPUT);
  digitalWrite (pinReset, HIGH);

  for (uint8_t i = 20; i; i--)
    {
      digitalWrite (ledPin, LOW); delay (15);
      digitalWrite (ledPin, HIGH); delay (150);
    }

  while (!initNet())
    delay (500);

  k.begin();

  while (!resetRadio())
    {
      k.debug ("Radio init failed");
      delay (1000);
    }
  k.debug ("Radio init success");

  digitalWrite (ledPin, LOW); delay(5);
  digitalWrite (ledPin, HIGH); delay(100);
  digitalWrite (ledPin, LOW); delay(15);
  digitalWrite (ledPin, HIGH); delay(300);
}


void loop (void)
{
  k.loop();

  const unsigned long int now = millis();
  static unsigned long int nextReset = 0;

  if (lastactivity == 0)
    lastactivity = now;

  // dunwerk
  if ((now > lastactivity) && ((now - lastactivity) > WATCHDOG*1000L))
    {
      //  ESP.restart();
      k.debug ("something's up");
      lastactivity = now;
    }

  if (now >= nextReset)
    {
      resetRadio();
      nextReset = now + 599000;
      k.debug ("resetting");	/* also something of a heartbeat signal.. */
    }
}

// ----------------------------------------------------------------------
